package org.g4studio.core.orm.xibatis.sqlmap.engine.type;

import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.lang.NoSuchMethodException;

/**
 * Implementation of TypeHandler for dealing with unknown types
 */
public class UnknownTypeHandler extends BaseTypeHandler implements TypeHandler {

	private TypeHandlerFactory factory;

	static private boolean usingJavaPre5 = false;

	static {
		try {
			// try getBaseClass, if it throws no exception
			// were in Java <5
			getBaseClass(Class.class);
			usingJavaPre5 = false;
		} catch (NoSuchMethodException ex) {
			usingJavaPre5 = true;
		}
	};

	/**
	 * Constructor to create via a factory
	 * 
	 * @param factory
	 *            - the factory to associate this with
	 */
	public UnknownTypeHandler(TypeHandlerFactory factory) {
		this.factory = factory;
	}

	public void setParameter(PreparedStatement ps, int i, Object parameter, String jdbcType) throws SQLException {
		Class searchClass = parameter.getClass();
		if (usingJavaPre5) {
			try {
				searchClass = getBaseClass(searchClass);
			} catch (Exception ex) {
				searchClass = null;
			}
		}
		if (searchClass == null) {
			searchClass = parameter.getClass();
		}
		TypeHandler handler = factory.getTypeHandler(searchClass, jdbcType);
		handler.setParameter(ps, i, parameter, jdbcType);
	}

	public Object getResult(ResultSet rs, String columnName) throws SQLException {
		Object object = rs.getObject(columnName);
		if (rs.wasNull()) {
			return null;
		} else {
			return object;
		}
	}

	public Object getResult(ResultSet rs, int columnIndex) throws SQLException {
		Object object = rs.getObject(columnIndex);
		if (rs.wasNull()) {
			return null;
		} else {
			return object;
		}
	}

	public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {
		Object object = cs.getObject(columnIndex);
		if (cs.wasNull()) {
			return null;
		} else {
			return object;
		}
	}

	public Object valueOf(String s) {
		return s;
	}

	public boolean equals(Object object, String string) {
		if (object == null || string == null) {
			return object == string;
		} else {
			TypeHandler handler = factory.getTypeHandler(object.getClass());
			Object castedObject = handler.valueOf(string);
			return object.equals(castedObject);
		}
	}

	/**
	 * Get the base class of classParam, for top level classes this returns
	 * null. For enums, inner and anonymous classes it returns the enclosing
	 * class. The intent is to use this for enum support in Java 5+.
	 * 
	 * @param classParam
	 *            class to get enclosing class of
	 * @return Enclosing class
	 * @throws NoSuchMethodException
	 *             when run in pre Java 5.
	 */
	private static Class getBaseClass(Class classParam) throws NoSuchMethodException {
		String methodName = "getEnclosingClass";

		Method method = null;
		Class result = null;
		try {
			method = classParam.getClass().getMethod(methodName, (Class[]) null);
			result = (Class) method.invoke(classParam, (Object[]) null);
		} catch (Exception ex) {
			throw new NoSuchMethodException(ex.getMessage());
		}
		return result;
	}
}